#include "mqtt.h"
#include "print.h"
#include "define.h"
#include "base64.h"
#include <string>
//////////////////////////////////////////////////////////////////////////////////////////////////

void(*restart)(void) = 0;

//////////////////////////////////////////////////////////////////////////////////////////////////

const char* MqttClient::c2sHTopic = "/arduino/ctos/";
const char* MqttClient::s2cHTopic = "/arduino/stoc/";

//////////////////////////////////////////////////////////////////////////////////////////////////

MqttClient::MqttClient()
    : client(nullptr)
    , choose(-1)
    , failCnt(0)
    , subSuccess(false) {
}

MqttClient::~MqttClient() {
    if (client != nullptr) {
        if (client->connected() == true) {
            client->disconnect();
        }
        delete client;
        client = nullptr;
    }
}

void MqttClient::setup() {
    Serial.begin(9600);

    WiFi.mode(WIFI_STA);
    WiFi.disconnect();
    delay(100);

    recvData = "";
}

void MqttClient::loop() {
    while (choose < 0 || choose >= g_servercfg_cnt) {
        scan();
        if (choose < 0 || choose >= g_servercfg_cnt) {
            failCnt++;
            if (failCnt > 10) {
                restart();
            }
            delay(100);
        } else {
            failCnt = 0;
        }
    }

    if (client == nullptr) {
        auto& scfg = g_servercfg[choose];
        //println("connect to server: ", scfg.server, ", port: ", scfg.port, ", ssid: ", scfg.ssid, ", password: ", scfg.passwd);

        WiFi.begin(scfg.ssid, scfg.passwd);
        while (WiFi.status() != WL_CONNECTED) {
            delay(500);
            //print(".");
        }

        smac = WiFi.macAddress();
        smac.replace(':', '-');
        subscribe("[MAC]" + smac);

        c2sTopic = c2sHTopic + smac;
        s2cTopic = s2cHTopic + smac;

        sip = "";
        auto lip = WiFi.localIP();
        for (auto i = 0; i < 4; i++) {
            sip += lip[i];
            if (i != 3) {
                sip += '.';
            }
        }

        //println("wifi connected, ip address: ", sip, ", mac: ", smac);
        subscribe("[IP]" + sip);

        if (client == nullptr) {
            client = new PubSubClient(espClient);
        }

        client->setServer(scfg.server, scfg.port);
        client->setCallback([this](char* topic, uint8_t* payload, unsigned int length) {
            callback(topic, payload, length);
        });
    }

    receive();
    
    if (client->connected() == false) {
        connect();
    }
    client->loop();
}

void MqttClient::receive() {
    char ch;
    while (Serial.available() > 0) {
        ch = (char)Serial.read();
        if(ch == 0) {
            break;
        }
        if (ch == '\n') {
            publish();
            continue;
        }
        recvData += ch;
    }
}

void MqttClient::publish() {
    auto lenth = recvData.length();
    if (lenth <= 0) {
        return;
    }
    auto str = recvData.c_str();

    auto index = 0;
    while(str[index] == ' ' || str[index] == '\t') {
        index++;
        if(index >= lenth) {
            recvData = "";
            return;
        }
    }

    str = str + index;

    if (lenth - index > 4) {
        for(auto p = 0; p < lenth - index - 4; p++) {
            auto ptr = str + p;
            if(*ptr == 'p' && *(ptr + 1) == 'u' && *(ptr + 2) == 'b' && *(ptr + 3) == '[') {
                str = str + 4;
                auto dlen = 0, i = 0;
                for (; i < lenth; i++) {
                    if (str[i] == ']' && i > 0) {
                        for (auto step = 1, j = 0; j < i; j++) {
                            dlen += (str[i - j - 1] - '0') * step;
                            step *= 10;
                        }
                        break;
                    }
                }

                str = str + i + 1;
                if (dlen > 0 && lenth >= dlen + i + 1) {
                    if(*str == '#') {
                        deal(str + 1, dlen - 1);
                    } else {
                        if (client->connected() == true) {
                            client->publish(c2sTopic.c_str(), (const uint8_t*)str, dlen);
                        }
                    }
                }
                break;
            }
        }
        
    }

    recvData = "";
}

void MqttClient::deal(const char* msg, unsigned int len) {
    if(len >= 5 && msg[0] == 's' && msg[1] == 't' && msg[2] == 'a' && msg[3] == 'r' && msg[4] == 't') {
        subscribe("[state]");
        return;
    }

    if(len > 5 && msg[0] == 's' && msg[1] == 't' && msg[2] == 'a' && msg[3] == 't' && msg[4] == 'e') {
        String info = "";
        info = smac + "|" + sip + "|";
        for(auto i = 5; i < len; i++) {
            info += msg[i];
        }
        println("8266: state resp: ", info);
        if (client->connected() == true) {
            client->publish(c2sTopic.c_str(), (const uint8_t*)info.c_str(), (unsigned int)info.length());
        }
        return;
    }
}

void MqttClient::scan() {
    std::set<std::string> ssidst;
    //println("scan wifi ......");
    auto cnt = WiFi.scanNetworks();
    for (auto i = 0; i < cnt; i++) {
        auto ssid = WiFi.SSID(i);
        if (ssid.length() <= 0) {
            continue;
        }
        ssidst.insert(ssid.c_str());
        //println(i + 1, ": ", ssid, " (", WiFi.RSSI(i), ") ", WiFi.channel(), " ", (WiFi.encryptionType(i) == ENC_TYPE_NONE ? " " : "*"));
    }

    choose = -1;
    for (auto i = 0; i < g_servercfg_cnt; i++) {
        auto& scfg = g_servercfg[i];
        if (ssidst.count(scfg.ssid) <= 0) {
            continue;
        }
        choose = i;
    }
}

void MqttClient::connect() {
    while (client->connected() == false) {
        if (client->connect(smac.c_str())) {
            subSuccess = client->subscribe(s2cTopic.c_str(), 1);
            subscribe("[START]");
            //println("mqtt connection connected, subscribe topic: ", s2cTopic, ", success: ", (subSuccess ? true : false));
            break;
        } else {
            println("mqtt connection failed, rc=", client->state(), ", try again in 5 seconds");
            delay(500);
        }
    }
}

void MqttClient::callback(char* topic, uint8_t* payload, unsigned int length) {
    if (topic == nullptr || payload == nullptr) {
        return;
    }
    if (length <= 0) {
        length = strlen((const char*)payload);
    }
    String info = base64_encode((const char*)payload, length);
    if (info.length() > 0) {
        subscribe(info);
    }
}

void MqttClient::subscribe(const String& data) {
    println("");
    println("sub[", data.length(), "]", data);
}